Addams Family - Pugsley's Scavenger Hunt, 
An explanation of the password system and how to generate valid codes to the NES game by Shawn Crawford [sLeEpY]


For debugging use FCEUX 2.1: http://fceux.com/web/htdocs/

Load the rom, goto the password screen, enter a 1, back to FCEUX, open tools, cheats, click "RESET", back to the game and leave the cheats window open, erase 1 and put in a 2, click the "not equal" button, there is 7 results:
$002E
$002F
$0040
$01F4
$0207
$020B
$043D

Now back at the game screen with cheats window open we see the first 4 results change values with the cursor, so this is where we get our password table:

Password Table: (Check the contents of A when entering a number/letter)
--------------------------------------------------------------------------------
1   2   3   4   5   6   7   8   9   B   C    D    F    G    H    J	<-password letter/number
--------------------------------------------------------------------------------
0   1   2   3   4   5   6   7   8   9   10   11   12   13   14   15	<-actual value
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
K    L    M    N    P    R    S    T    V    W    X    Y    Z        ?    -	<-password letter/number
--------------------------------------------------------------------------------
16   17   18   19   20   21   22   23   24   25   26   27   28   29   30   31	<-actual value
--------------------------------------------------------------------------------

So now we backspace and notice that the value at $043D changes to FF. If we select a letter the value of the letter shows in place of FF.

Next start the debugger. 

Here we can set a breakpoint on read $043D to find the password routine, next enter the password and it will break, once it breaks set a breakpoint on write (BPW) on $0441, 
and clear the other breakpoints. Click "Run" and it will break again, ($043D holds first letter, $043E holds second, $043F holds 3rd, $0440 holes fourth, $0441 holds fifth),

Now we have a clear view of the decoding routine. You can see if start after RTS and end at RTS. (return from subroutine)

00:BAC7:60        RTS			<-from some other subroutine ending

00:BAC8:AD 3D 04  LDA $043D = #$01	<-load first password char (from $043D)
00:BACB:18        CLC
00:BACC:6D 3E 04  ADC $043E = #$01	<-add second password char to it (from $043E)
00:BACF:18        CLC
00:BAD0:6D 3F 04  ADC $043F = #$00	<-add third password char to it (from $043F)
00:BAD3:18        CLC
00:BAD4:6D 40 04  ADC $0440 = #$00	<-add fourth password char to it, total is checksum (from $0440)
00:BAD7:18        CLC
00:BAD8:69 25     ADC #$25		<-add 25h to the checksum
00:BADA:29 1F     AND #$1F		<-AND the checksum with 1F
00:BADC:CD 41 04  CMP $0441 = #$09	<-compare final checksum with last password char
00:BADF:D0 3A     BNE $BB1B		<-if password check fails, jump and return -1
00:BAE1:AD 3D 04  LDA $043D = #$01	<-load first password char (from $043D)
00:BAE4:49 0A     EOR #$0A		<-XOR with 0Ah
00:BAE6:0A        ASL			<-shift left 1
00:BAE7:0A        ASL			<-shift left 1
00:BAE8:0A        ASL			<-shift left 1
00:BAE9:0A        ASL			<-shift left 1
00:BAEA:8D 3B 04  STA $043B = #$00	<-store first "encoded" char at $043B
00:BAED:AD 3E 04  LDA $043E = #$01	<-load second password char (from $043E)
00:BAF0:49 0F     EOR #$0F		<-XOR with 0F
00:BAF2:4A        LSR			<-shift right 1
00:BAF3:0D 3B 04  ORA $043B = #$00	<-OR with value stored at $043B
00:BAF6:8D 3B 04  STA $043B = #$00	<-store char in $043B
00:BAF9:29 02     AND #$02		<-AND with 02h, this is for the bit 1 check
00:BAFB:D0 1E     BNE $BB1B		<-is bit 1 set? If so then password check fails, jump and return -1
00:BAFD:AD 3F 04  LDA $043F = #$00	<-load third password char (from $043F)
00:BB00:49 05     EOR #$05		<-XOR with 05h
00:BB02:8D 37 04  STA $0437 = #$00	<-store value at $0437
00:BB05:AD 40 04  LDA $0440 = #$00	<-load fourth password char (from $0440)
00:BB08:49 02     EOR #$02		<-XOR with 02h
00:BB0A:4A        LSR			<-shift right 1
00:BB0B:8D 38 04  STA $0438 = #$05	<-store value at $0438
00:BB0E:A9 08     LDA #$08
00:BB10:8D 47 06  STA $0647 = #$09
00:BB13:A9 02     LDA #$02
00:BB15:8D 49 06  STA $0649 = #$00
00:BB18:A9 00     LDA #
00:BB1A:60        RTS




Now for an example we will enter password 12345. Open the debugger and set a breakpoint on $BADC, also open the trace logger and click start, now we can see all the info.

$B57B:20 C8 BA  JSR $BAC8                  A:04 X:04 Y:05 P:nvUbdIZC
$BAC8:AD 3D 04  LDA $043D = #$00           A:04 X:04 Y:05 P:nvUbdIZC <-load value for first char
$BACB:18        CLC                        A:00 X:04 Y:05 P:nvUbdIZC <-value loaded in A (first char is 1, value is 0h)
$BACC:6D 3E 04  ADC $043E = #$01           A:00 X:04 Y:05 P:nvUbdIZc <-add value for second char
$BACF:18        CLC                        A:01 X:04 Y:05 P:nvUbdIzc <-value loaded in A (2nd char is 2, value is 1h, 1h + 0h = 1h)
$BAD0:6D 3F 04  ADC $043F = #$02           A:01 X:04 Y:05 P:nvUbdIzc <-add value for third char
$BAD3:18        CLC                        A:03 X:04 Y:05 P:nvUbdIzc <-value loaded in A (3rd char is 3, value is 2h, 1h + 2h = 3h)
$BAD4:6D 40 04  ADC $0440 = #$03           A:03 X:04 Y:05 P:nvUbdIzc <-load value for 4th char
$BAD7:18        CLC                        A:06 X:04 Y:05 P:nvUbdIzc <-value loaded in A (4th char is 4, value is 3h, 3h + 3h = 6h), CLC is clear the carry flag
$BAD8:69 25     ADC #$25                   A:06 X:04 Y:05 P:nvUbdIzc <-add 25h
$BADA:29 1F     AND #$1F                   A:2B X:04 Y:05 P:nvUbdIzc <-A now has 2Bh (37d + 6d = 43d, or 2Bh), AND this result with 1Fh
$BADC:CD 41 04  CMP $0441 = #$04           A:0B X:04 Y:05 P:nvUbdIzc <-A now has 0Bh, this is our checksum, compare 0Bh (D) with fifth password letter 5 (4), it 
								       is not equal so jump and fail, so the correct pass to get past this flag would be 1234D
$BADF:D0 3A     BNE $BB1B					     <-jump and fail

so retype the password with breakpoint on BADC now switching to BADF, and tryng the password 1234D:
$B57B:20 C8 BA  JSR $BAC8                  A:0B X:03 Y:05 P:nvUbdIZC
$BAC8:AD 3D 04  LDA $043D = #$00           A:0B X:03 Y:05 P:nvUbdIZC
$BACB:18        CLC                        A:00 X:03 Y:05 P:nvUbdIZC
$BACC:6D 3E 04  ADC $043E = #$01           A:00 X:03 Y:05 P:nvUbdIZc
$BACF:18        CLC                        A:01 X:03 Y:05 P:nvUbdIzc
$BAD0:6D 3F 04  ADC $043F = #$02           A:01 X:03 Y:05 P:nvUbdIzc
$BAD3:18        CLC                        A:03 X:03 Y:05 P:nvUbdIzc
$BAD4:6D 40 04  ADC $0440 = #$03           A:03 X:03 Y:05 P:nvUbdIzc
$BAD7:18        CLC                        A:06 X:03 Y:05 P:nvUbdIzc
$BAD8:69 25     ADC #$25                   A:06 X:03 Y:05 P:nvUbdIzc
$BADA:29 1F     AND #$1F                   A:2B X:03 Y:05 P:nvUbdIzc
$BADC:CD 41 04  CMP $0441 = #$0B           A:0B X:03 Y:05 P:nvUbdIzc
$BADF:D0 3A     BNE $BB1B                  A:0B X:03 Y:05 P:nvUbdIZC <-checksum passes, so now we calc the rest
$BAE1:AD 3D 04  LDA $043D = #$00           A:0B X:03 Y:05 P:nvUbdIZC
$BAE4:49 0A     EOR #$0A                   A:00 X:03 Y:05 P:nvUbdIZC
$BAE6:0A        ASL                        A:0A X:03 Y:05 P:nvUbdIzC
$BAE7:0A        ASL                        A:14 X:03 Y:05 P:nvUbdIzc
$BAE8:0A        ASL                        A:28 X:03 Y:05 P:nvUbdIzc
$BAE9:0A        ASL                        A:50 X:03 Y:05 P:nvUbdIzc
$BAEA:8D 3B 04  STA $043B = #$A7           A:A0 X:03 Y:05 P:NvUbdIzc
$BAED:AD 3E 04  LDA $043E = #$01           A:A0 X:03 Y:05 P:NvUbdIzc
$BAF0:49 0F     EOR #$0F                   A:01 X:03 Y:05 P:nvUbdIzc
$BAF2:4A        LSR                        A:0E X:03 Y:05 P:nvUbdIzc
$BAF3:0D 3B 04  ORA $043B = #$A0           A:07 X:03 Y:05 P:nvUbdIzc
$BAF6:8D 3B 04  STA $043B = #$A0           A:A7 X:03 Y:05 P:NvUbdIzc
$BAF9:29 02     AND #$02                   A:A7 X:03 Y:05 P:NvUbdIzc
$BAFB:D0 1E     BNE $BB1B						<-A = A7h, it needs to be 0 in order to pass (BNE   Branch on Result not Zero)

This password fails the second check:

second char in this example is 2 (which is value 1h)
second char is XOR'd with 0Fh (then store in X), shifted right one time, OR'd with X, AND'd with 02h.


01 = 00000001
OF = 00001111 (15)(XOR)
------------------
14 = 00001110
shift right 00001110 >> 00000111

07 = 00000111
14 = 00001110 (OR)
------------------
15 = 00001111

15 = 00001111  <-last bit is invalid
02 = 00000010 (AND) <-this check is made, if the result is not 0 then fail
------------------
02 = 00000010	<-the result is 2, so fails because we want 0



-----------------------------------------------------------------------------------------------------
Truth Table:
bit 1  	bit 2  	OR (|)  AND (&) XOR (^)
0 	0 	0 	0 	0
1 	0 	1 	0 	1
0 	1 	1 	0 	1
1 	1 	1 	1 	0
-----------------------------------------------------------------------------------------------------






Example of a valid password:
[CJ614]

$BAC8:AD 3D 04  LDA $043D = #$0A           A:03 X:03 Y:05 P:nvUbdIZC <-load value for first char
$BACB:18        CLC                        A:0A X:03 Y:05 P:nvUbdIzC <-value loaded in A (first char is C, value is Ah (10d))
$BACC:6D 3E 04  ADC $043E = #$0F           A:0A X:03 Y:05 P:nvUbdIzc <-add value for second char
$BACF:18        CLC                        A:19 X:03 Y:05 P:nvUbdIzc <-value loaded in A (2nd char is J, value is 15d, 15d + 10d = 25d (19h))
$BAD0:6D 3F 04  ADC $043F = #$05           A:19 X:03 Y:05 P:nvUbdIzc <-add value for third char
$BAD3:18        CLC                        A:1E X:03 Y:05 P:nvUbdIzc <-value loaded in A (3rd char is 6, value is 5d, 25d + 5d = 30d (1Eh))
$BAD4:6D 40 04  ADC $0440 = #$00           A:1E X:03 Y:05 P:nvUbdIzc <-load value for 4th char
$BAD7:18        CLC                        A:1E X:03 Y:05 P:nvUbdIzc <-value loaded in A (4th char is 1, value is 0, 0 + 30 = 30d (1Eh)), CLC is clear the carry flag
$BAD8:69 25     ADC #$25                   A:1E X:03 Y:05 P:nvUbdIzc <-add 25h (37d)
$BADA:29 1F     AND #$1F                   A:43 X:03 Y:05 P:nvUbdIzc <-A now has 43h (37d + 30d = 67d, or 43h), AND this result with 1F
$BADC:CD 41 04  CMP $0441 = #$03           A:03 X:03 Y:05 P:nvUbdIzc <-A now has 03, this is our checksum, compare 03 (password letter 4) with fifth password char 03 (password letter 4)
$BADF:D0 3A     BNE $BB1B                  A:03 X:03 Y:05 P:nvUbdIZC <-all ok, jump is not taken
$BAE1:AD 3D 04  LDA $043D = #$0A           A:03 X:03 Y:05 P:nvUbdIZC <-load first password char (from $043D), password letter C, value 0Ah (10d)
$BAE4:49 0A     EOR #$0A                   A:0A X:03 Y:05 P:nvUbdIzC <-XOR that value with 0A, result is 0
$BAE6:0A        ASL                        A:00 X:03 Y:05 P:nvUbdIZC <-shift left 1
$BAE7:0A        ASL                        A:00 X:03 Y:05 P:nvUbdIZc <-shift left 1
$BAE8:0A        ASL                        A:00 X:03 Y:05 P:nvUbdIZc <-shift left 1
$BAE9:0A        ASL                        A:00 X:03 Y:05 P:nvUbdIZc <-shift left 1
$BAEA:8D 3B 04  STA $043B = #$A7           A:00 X:03 Y:05 P:nvUbdIZc <-store first "encoded" char at $043B, which is 0
$BAED:AD 3E 04  LDA $043E = #$0F           A:00 X:03 Y:05 P:nvUbdIZc <-load second password char (from $043E), J, value 0Fh (15d)
$BAF0:49 0F     EOR #$0F                   A:0F X:03 Y:05 P:nvUbdIzc <-XOR with 0Fh(15d) result is 0 
$BAF2:4A        LSR                        A:00 X:03 Y:05 P:nvUbdIZc <-shift right 1
$BAF3:0D 3B 04  ORA $043B = #$00           A:00 X:03 Y:05 P:nvUbdIZc <-OR with value stored at $043B, result is 0
$BAF6:8D 3B 04  STA $043B = #$00           A:00 X:03 Y:05 P:nvUbdIZc <-store char in $043B
$BAF9:29 02     AND #$02                   A:00 X:03 Y:05 P:nvUbdIZc <-AND with 02, this is for the bit 1 check, result is 0
$BAFB:D0 1E     BNE $BB1B                  A:00 X:03 Y:05 P:nvUbdIZc <-result is 0, no jump taken
$BAFD:AD 3F 04  LDA $043F = #$05           A:00 X:03 Y:05 P:nvUbdIZc <-load third password char (from $043F), 6 (value 5d)
$BB00:49 05     EOR #$05                   A:05 X:03 Y:05 P:nvUbdIzc <-XOR with 05, result is 0
$BB02:8D 37 04  STA $0437 = #$00           A:00 X:03 Y:05 P:nvUbdIZc <-store value at $0437
$BB05:AD 40 04  LDA $0440 = #$00           A:00 X:03 Y:05 P:nvUbdIZc <-load fourth password char (from $0440), 1 (value 0)
$BB08:49 02     EOR #$02                   A:00 X:03 Y:05 P:nvUbdIZc <-XOR with 02, result 02
$BB0A:4A        LSR                        A:02 X:03 Y:05 P:nvUbdIzc <-shift right 1, result is 01
$BB0B:8D 38 04  STA $0438 = #$05           A:01 X:03 Y:05 P:nvUbdIzc <-store value at $0438
$BB0E:A9 08     LDA #$08                   A:01 X:03 Y:05 P:nvUbdIzc
$BB10:8D 47 06  STA $0647 = #$09           A:08 X:03 Y:05 P:nvUbdIzc
$BB13:A9 02     LDA #$02                   A:08 X:03 Y:05 P:nvUbdIzc
$BB15:8D 49 06  STA $0649 = #$00           A:02 X:03 Y:05 P:nvUbdIzc
$BB18:A9 00     LDA #$00                   A:02 X:03 Y:05 P:nvUbdIzc



So for password CJ61, the 5th char would be:
10+15+5+0 = 30
Add 25h (37d)
30 + 37 = 67 (43h)
AND with 1F
67 in binary is: 01000011
1F = 31d, in binary is: 00011111

01000011
00011111 AND
---------
00000011
results are: 00000011, which is 03

03 on the chart is 4.

So the last char is 4.

Now we need to create a password generator program. I coded this one in visual studio 2008 using c#.

What we can figure from the information given:

1.The first password char is left shifted 4 times, XOR with 0A.
2.The second password char is right shifted 1 time, XOR with 0F
3.The third password char is XOR'd with 05.
4.The fourth password char is left shifted 1 time, XOR with 02.
5.Final password char must equal the first 4 chars added together, then add 25, then AND'd with 1F.

This is just the button on the form that generates the code. Also it will be slightly different as the values that determine hearts, saved people, etc, have been implemented.


        private void generateButton_Click(object sender, EventArgs e)
        {
            int numLivesStart = 0, errorCounter = 0;
            int numLives;
            string numLivesString, pass1, pass2, pass3, pass4, pass5;
            int tmpPass1 = 0, tmpPass2 = 0, tmpPass3 = 0, tmpPass4 = 0, tmpPass5 = 0;

            try
            {
                //digit 3 and 4 are the number of lives
                numLivesStart = int.Parse(livesComboBox.Text); //<-parse the number of lives
            }
            catch
            {
                MessageBox.Show("Numeric numbers in the lives value only please.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                errorCounter = 1; //ignore for now
            }

            numLivesString = numLivesStart.ToString();

            //this gives us the dec value of the char
            numLives = int.Parse(numLivesString);

            tmpPass3 = ((numLives & 0x1F) >> 4) ^ 5; //third password char is the number of lives AND'd with 1Fh, right shifted 4, XOR'd 5
            tmpPass4 = ((numLives & 0x0F) << 1) ^ 2; //fourth password char is the number of lives AND'd with 0Fh, left shifted 1, XOR'd 2
            
            if (random3CheckBox.Checked)
            {
                tmpPass4 = (tmpPass4 | 1); //3rd random, not necessary but if it's set then 4th password char is OR'd with 1
            }

            if (random1CheckBox.Checked)
            {
                tmpPass1 += 1 << (4); //1st random, also not necessary but if it's set then add 1 and left shift 4
            }
            if (heart1CheckBox.Checked)
            {
                tmpPass1 += 1 << (3); //3 hearts in the game, if first is ticked add 1 and left shift 3
            }
            if (heart2CheckBox.Checked)
            {
                tmpPass1 += 1 << (2); //add 1 and left shift 2
            }
            if (heart3CheckBox.Checked)
            {
                tmpPass1 += 1 << (1); //add 1 and left shift 1
            }
            if (wednesdayCheckBox.Checked)
            {
                tmpPass1 += 1; //add 1
            }

            if (grannyCheckBox.Checked)
            {
                tmpPass2 |= 0x10; //OR with 10h (16d) (this is the same as right shift, ex: 10000 (16) >>(1) = 01000(8))
            }
            if (uncleFesterCheckBox.Checked)
            {
                tmpPass2 |= 8; //OR with 8
            }
            if (gomezCheckBox.Checked)
            {
                tmpPass2 |= 2; //OR with 2
            }
            if (random2CheckBox.Checked)
            {
                tmpPass2 |= 1; //OR with 1
            }

            tmpPass1 = tmpPass1 ^ 0x0A; //XOR with 0x0A
            tmpPass2 = tmpPass2 ^ 0x0F; //XOR with 0x0F

	    //5.Final password char must equal the first 4 chars added together, then add 25, then AND'd with 1F.
            tmpPass5 = (tmpPass1 + tmpPass2 + tmpPass3 + tmpPass4 + tmpPass5 + 0x25) & 0x1F;

	    //AddamsConvert is just a function the translates the values back to the games password characters
            tempDigitString = tmpPass1.ToString();
            AddamsConvert();
            pass1 = AddamsConvert();

            tempDigitString = tmpPass2.ToString();
            AddamsConvert();
            pass2 = AddamsConvert();

            tempDigitString = tmpPass3.ToString();
            AddamsConvert();
            pass3 = AddamsConvert();

            tempDigitString = tmpPass4.ToString();
            AddamsConvert();
            pass4 = AddamsConvert();

            tempDigitString = tmpPass5.ToString();
            AddamsConvert();
            pass5 = AddamsConvert();

            passwordTextBox.Text = pass1 + pass2 + pass3 + pass4 + pass5;


        }


	//function from above
        private string AddamsConvert()
        {

            if (tempDigitString == "0")
            {
                passwordDigitString = "1";
            }
            else if (tempDigitString == "1")
            {
                passwordDigitString = "2";
            }
            else if (tempDigitString == "2")
	....
	....
	....
            else if (tempDigitString == "30")
            {
                passwordDigitString = "?";
            }
            else if (tempDigitString == "31")
            {
                passwordDigitString = "-";
            }

            return passwordDigitString;
        }


Download Source Code for VS2009.

























